package com.arges.controller;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 获取参数签名
 *
 * @author 壹证通
 */
public class GetSignature {

    /**
     * 密钥
     */
    private static final String secretKey = "AiFsi7k315BBRboMcLRsl1dgxtdp1z4j";
    private static final String charset = "UTF-8";
    private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String ALGORITHM = "HmacSHA256";
    private static final Object LOCK = new Object();
    private static Mac macInstance;

    public static void main(String[] args) {


        Map<String, String> paramMap = new HashMap<String, String>();

        //公共参数
        paramMap.put("appKey", "72tBHkKQQn1zE5TN");
        paramMap.put("signMethod", "HmacSHA256");
        paramMap.put("signVersion", "1");
        paramMap.put("format", "JSON");
        paramMap.put("t", getTime());
        paramMap.put("v", "1");
        paramMap.put("nonce", getNonce());

//        //具体接口参数,如姓名实名认证
//        paramMap.put("realname", "");
//        paramMap.put("idcard", "XXX");

        //获取待签名内容,排序
        String signContent = getSignatureContent(paramMap);
        System.out.println("待签名内容：" + signContent);

        //计算签名
        String sign = computeSignature(secretKey, signContent, charset);
        System.out.println("签名后：" + sign);
    }

    /**
     * 格式化时间
     */
    private static String getTime() {
        TimeZone tz = TimeZone.getTimeZone("UTC");
        DateFormat df = new SimpleDateFormat(TIMESTAMP_FORMAT);
        df.setTimeZone(tz);
        return df.format(new Date());
    }

    /**
     * 生成随机数
     */
    private static String getNonce() {
        Random random = new Random();
        return String.valueOf(random.nextInt(1000000000));
    }

    /**
     * 将参数按key值排序
     */
    public static String getSignatureContent(Map<String, String> paramMap) {

        Collection<String> keySet = paramMap.keySet();
        //签名内容
        StringBuilder content = new StringBuilder();
        //所有的键值
        List<String> keys = new ArrayList<String>(keySet);
        //排序
        Collections.sort(keys);

        //循环赋值
        for (String key : keys) {
            String value = paramMap.get(key);
            if (isNotEmpty(key) && isNotEmpty(value)) {
                content.append(key).append(value);
            }
        }

        return content.toString();
    }

    /**
     * 字符串非空校验
     */
    private static boolean isNotEmpty(String str) {
        return str != null && str.length() != 0;
    }

    /**
     * 计算签名值
     */

    private static String computeSignature(String key, String data, String charset) {
        try {
            byte[] signData = sign(key.getBytes(charset), data.getBytes(charset));
            return byte2hex(signData);
        } catch (UnsupportedEncodingException ex) {
            throw new RuntimeException("不支持的算法: " + charset, ex);
        }
    }

    /**
     * 把字节流转换为十六进制表示方式。
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(aByte & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }

        return sign.toString();
    }

    /**
     * 使用HMAC加密
     */
    private static byte[] sign(byte[] key, byte[] data) {
        try {
            //因为Mac类的getInstance()方法的调用时一个同步方法，可能被阻塞，所以使用原型模式来提高可靠性
            if (macInstance == null) {
                synchronized (LOCK) {
                    if (macInstance == null) {
                        macInstance = Mac.getInstance(ALGORITHM);
                    }
                }
            }

            Mac mac;
            try {
                mac = (Mac) macInstance.clone();
            } catch (CloneNotSupportedException e) {
                //如果不可复制，创建一个新的Mac对象
                mac = Mac.getInstance(ALGORITHM);
            }

            mac.init(new SecretKeySpec(key, ALGORITHM));
            return mac.doFinal(data);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException("不支持的算法: " + ALGORITHM, ex);
        } catch (InvalidKeyException ex) {
            throw new RuntimeException("非法key: " + key, ex);
        }
    }
}